﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Csla;
using System.Data;
using System.IO;
using System.Web;
using Csla.Data;
using Csla.Validation;
using System.Data.Common;
using System.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Data;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using System.Threading;

namespace Asah.SiteWorx.SiteMap
{
    [Serializable()]
    public class Page : Csla.BusinessBase<Page>
    {

        #region Business Methods

        private int _id = 0;
        private string _title = "";
        private string _fileName = "";
        private string _url = "";
        private string _metaKeywords = "";
        private string _metaDescription = "";
        private string _menuText = "";
        private string _menuHoverText = "";
        private bool _displayInMenu = true;
        private bool _displayInMenuChanged = false;
        private int _menuPosition = -1;
        private string _masterPage = "";
        private PageVisibility _visibility = PageVisibility.Public;
        private int _parentPageId = -1;
        private Membership.User _createdBy;
        private SmartDate _createdDate;
        private Membership.User _lastUpdatedBy;
        private SmartDate _lastUpdatedDate;

        [System.ComponentModel.DataObjectField(true,true)]
        public int ID
        {
            get
            {
                CanReadProperty("ID", true);
                return _id;
            }
        }

        public string Title
        {
            get
            {
                CanReadProperty("Title", true);
                if (_title.Length == 0)
                {
                    if (ConfigurationManager.AppSettings["DefaultPageTitle"] != null && 
                        ConfigurationManager.AppSettings["DefaultPageTitle"].ToString().Length > 0)
                    {
                        _title = ConfigurationManager.AppSettings["DefaultPageTitle"].ToString();
                    }
                }
                return _title;
            }
            set
            {
                CanWriteProperty("Title", true);
                if (_title != value)
                {
                    _title = value;
                    PropertyHasChanged("Title");
                }
            }
        }

        public string FileName
        {
            get
            {
                CanReadProperty("FileName", true);
                return _fileName;
            }
            set
            {
                CanWriteProperty("FileName", true);
                if (_fileName != value)
                {
                    _fileName = value;
                    PropertyHasChanged("FileName");
                }
            }
        }
        
        public string Url
        {
            get
            {
                CanReadProperty("Url", true);
                return _url;
            }
        }

        public string MetaKeywords
        {
            get
            {
                CanReadProperty("MetaKeywords", true);
                return _metaKeywords;
            }
            set
            {
                CanWriteProperty("MetaKeywords", true);
                if (_metaKeywords != value)
                {
                    _metaKeywords = value;
                    PropertyHasChanged("MetaKeywords");
                }
            }
        }

        public string MetaDescription
        {
            get
            {
                CanReadProperty("MetaDescription", true);
                return _metaDescription;
            }
            set
            {
                CanWriteProperty("MetaDescription", true);
                if (_metaDescription != value)
                {
                    _metaDescription = value;
                    PropertyHasChanged("MetaDescription");
                }
            }
        }

        public string MenuText
        {
            get
            {
                CanReadProperty("MenuText", true);
                return _menuText;
            }
            set
            {
                CanWriteProperty("MenuText", true);
                if (_menuText != value)
                {
                    _menuText = value;
                    PropertyHasChanged("MenuText");
                }
            }
        }

        public string MenuHoverText
        {
            get
            {
                CanReadProperty("MenuHoverText", true);
                return _menuHoverText;
            }
            set
            {
                CanWriteProperty("MenuHoverText", true);
                if (_menuHoverText != value)
                {
                    _menuHoverText = value;
                    PropertyHasChanged("MenuHoverText");
                }
            }
        }

        public bool DisplayInMenu
        {
            get
            {
                CanReadProperty("DisplayInMenu", true);
                return _displayInMenu;
            }
            set
            {
                CanWriteProperty("DisplayInMenu", true);
                if (_displayInMenu != value)
                {
                    _displayInMenuChanged = true;
                    _displayInMenu = value;
                    PropertyHasChanged("DisplayInMenu");
                }
            }
        }

        public int MenuPosition
        {
            get
            {
                CanReadProperty("MenuPosition", true);
                return _menuPosition;
            }
        }

        public string MasterPage
        {
            get
            {
                CanReadProperty("MasterPage", true);
                return _masterPage;
            }
            set
            {
                CanWriteProperty("MasterPage", true);
                if (_masterPage != value)
                {
                    _masterPage = value;
                    PropertyHasChanged("MasterPage");
                }
            }
        }

        public PageVisibility Visibility
        {
            get
            {
                CanReadProperty("Visibility", true);
                return _visibility;
            }
            set
            {
                CanWriteProperty("Visibility", true);
                if (_visibility != value)
                {
                    _visibility = value;
                    PropertyHasChanged("Visibility");
                }
            }
        }

        public int ParentPageId
        {
            get
            {
                CanReadProperty("ParentPageId", true);
                return _parentPageId;
            }
        }

        public Membership.User CreatedBy
        {
            get
            {
                CanReadProperty("CreatedBy", true);
                return _createdBy;
            }
        }

        public string CreatedDate
        {
            get
            {
                CanReadProperty("CreatedDate", true);
                return _createdDate.ToString("g");
            }
        }

        public Membership.User LastUpdatedBy
        {
            get
            {
                CanReadProperty("LastUpdatedBy", true);
                return _lastUpdatedBy;
            }
        }

        public string LastUpdatedDate
        {
            get
            {
                CanReadProperty("LastUpdatedDate", true);
                return _lastUpdatedDate.ToString("g");
            }
        }

        protected override object GetIdValue()
        {
            return _id;
        }

        #endregion

        #region Validation Rules

        protected override void AddBusinessRules()
        {
            // add BusinessRules here
            ValidationRules.AddInstanceRule(CheckPageTitle, "Title");
            ValidationRules.AddInstanceRule(CheckFileExists, "FileName");
            ValidationRules.AddInstanceRule(CheckMenuText, "MenuText");
            ValidationRules.AddInstanceRule(CheckMasterPage, "MasterPage");
        }

        private bool CheckPageTitle(object target, Csla.Validation.RuleArgs e)
        {
            if (_title.Length > 0)
            {
                return true;
            }
            else
            {
                e.Description = "Page title is required.";
                return false;
            }
        }

        private bool CheckFileExists(object target, Csla.Validation.RuleArgs e)
        {
            if (_fileName.Length > 0)
            {
                string fileName = _fileName;

                // first check if we need to append the aspx extension to the filename.
                if (!fileName.Contains(".aspx"))
                {
                    fileName += ".aspx";
                }

                if (File.Exists(HttpContext.Current.Server.MapPath("~/") + fileName))
                {
                    e.Description = "A page by that file name already exists.";
                    return false;
                }
                else
                {
                    return true;
                }
            }
            else
            {
                e.Description = "File name is required.";
                return false;
            }
        }

        private bool CheckMenuText(object target, Csla.Validation.RuleArgs e)
        {
            if (_menuText.Length > 0)
            {
                return true;
            }
            else
            {
                e.Description = "Menu text is required.";
                return false;
            }
        }

        private bool CheckMasterPage(object target, Csla.Validation.RuleArgs e)
        {
            if (_masterPage.Length > 0)
            {
                return true;
            }
            else
            {
                e.Description = "Master layout is required.";
                return false;
            }
        }
       
        #endregion

        #region Authorization Rules

        protected override void AddAuthorizationRules()
        {
            // add AuthorizationRules here
        }

        public bool CanEditDisplayInMenu()
        {
            return !IsDefaultPage(_fileName);
        }

        public bool CanEditMasterPage()
        {
            return !IsDefaultPage(_fileName);
        }

        public bool CanEditVisibility()
        {
            return !IsDefaultPage(_fileName);
        }

        public static bool CanAddPage()
        {
            return true;
        }

        public static bool CanGetPage()
        {
            return true;
        }

        public static bool CanDeletePage(string fileName)
        {
            return !IsDefaultPage(fileName);
        }

        public static bool CanEditPage()
        {
            return true;
        }

        private static bool IsDefaultPage(string fileName)
        {
            return fileName.ToUpper() == "DEFAULT.ASPX";
        }

        #endregion

        #region Factory Methods

        public static Page NewPage()
        {
            if (!CanAddPage())
            {
                throw new System.Security.SecurityException("User not authorized to add a page.");
            }
            Page page = null;
            page = DataPortal.Create<Page>(null);

            // after the creation of a page we need to refresh the site object.
            Site.GetSite().Refresh();

            return page;
        }

        public static Page GetPage(int id)
        {
            if (!CanGetPage())
            {
                throw new System.Security.SecurityException("User not authorized to view a page.");
            }
            return DataPortal.Fetch<Page>(new Criteria(id));
        }

        public static void DeletePage(int id, string url)
        {
            if (!CanDeletePage(url))
            {
                throw new System.Security.SecurityException("User not authorized to remove a page.");
            }
            DataPortal.Delete(new PageCriteria(id, url));

            // after the deletion of a page we need to refresh the site object.
            Site.GetSite().Refresh();
        }

        public override Page Save()
        {
            if (IsDeleted && !CanDeletePage(_fileName))
            {
                throw new System.Security.SecurityException("User not authorized to remove a page.");
            }
            else if (IsNew && !CanAddPage())
            {
                throw new System.Security.SecurityException("User not authorized to add a page.");
            }
            else if (!CanEditPage())
            {
                throw new System.Security.SecurityException("User not authorized to update a page.");
            }

            Page page = null;
            page = base.Save();

            // after the saving of a page we need to refresh the site object.
            Site.GetSite().Refresh();

            return page;
        }

        private Page()
        {
            // require use of factory methods
        }
       
        #endregion

        #region Data Access

        [Serializable()]
        private class Criteria
        {
            private int _id;

            public int ID
            {
                get
                {
                    return _id;
                }
            }

            public Criteria(int id)
            {
                _id = id;
            }
        }

        [Serializable()]
        private class PageCriteria
        {
            private int _id;
            private string _path;

            public int ID
            {
                get
                {
                    return _id;
                }
            }

            public string Path
            {
                get
                {
                    return _path;
                }
            }

            public PageCriteria(int id, string path)
            {
                _id = id;
                _path = path;
            }
        }

        [RunLocal()]
        private void DataPortal_Create(Criteria criteria)
        {
            // add initial data to the object here
            // check all the validation rules
            ValidationRules.CheckRules();
        }

        private void DataPortal_Fetch(Criteria criteria)
        {
            Database db = DatabaseFactory.CreateDatabase("SiteWorx");
            DbCommand dbCommand = db.GetStoredProcCommand("aspnet_SiteMap_GetPage");
            db.AddInParameter(dbCommand, "ApplicationId", DbType.Guid, Site.GetSite().ApplicationId);
            db.AddInParameter(dbCommand, "PageId", DbType.Int32, criteria.ID);

            try
            {
                using (var dr = new SafeDataReader(db.ExecuteReader(dbCommand)))
                {
                    if (dr.Read())
                    {
                        _id = dr.GetInt32("PageId");
                        _title = dr.GetString("Title");
                        _fileName = dr.GetString("FileName");
                        _url = dr.GetString("Url");
                        _metaKeywords = dr.GetString("MetaKeywords");
                        _metaDescription = dr.GetString("MetaDescription");
                        _menuText = dr.GetString("MenuText");
                        _menuHoverText = dr.GetString("MenuHoverText");
                        _displayInMenu = dr.GetBoolean("DisplayInMenu");
                        _masterPage = dr.GetString("MasterPage");
                        _visibility = (PageVisibility) dr.GetInt32("Visibility");
                        _parentPageId = dr.GetInt32("ParentPageId");
                        _createdBy = Membership.User.GetUser(dr.GetGuid("CreatedBy"));
                        _createdDate = dr.GetSmartDate("CreatedDate");
                        _lastUpdatedBy = Membership.User.GetUser(dr.GetGuid("LastUpdatedBy"));
                        _lastUpdatedDate = dr.GetSmartDate("LastUpdatedDate");
                    }
                }
            }
            catch (Exception ex)
            {
                bool rethrow = ExceptionPolicy.HandleException(ex, "Data Access Layer Policy");
                if (rethrow)
                {
                    throw;
                }
            }
        }

        protected override void DataPortal_Insert()
        {
            // first check if we need to append the aspx extension to the filename.
            if (!_fileName.Contains(".aspx"))
            {
                _fileName += ".aspx";
            }

            StringBuilder htmlCode = new StringBuilder();
            htmlCode.AppendLine("<%@ Page Language=\"C#\" MasterPageFile=\"~/" + _masterPage + "\" Inherits=\"BasePage\" %>");
            htmlCode.AppendLine("<asp:Content ID=\"cntZone1\" ContentPlaceHolderID=\"cphZone1\" Runat=\"Server\">");
            htmlCode.AppendLine("<asp:WebPartZone ID=\"wpzZone1\" runat=\"server\" HeaderText=\"Zone 1\" Width=\"100%\">");
            htmlCode.AppendLine("<ZoneTemplate>");
            htmlCode.AppendLine("</ZoneTemplate>");
            htmlCode.AppendLine("</asp:WebPartZone>");
            htmlCode.AppendLine("</asp:Content>");

            File.WriteAllText(HttpContext.Current.Server.MapPath("~/") + _fileName, htmlCode.ToString());

            Database db = DatabaseFactory.CreateDatabase("SiteWorx");
            DbCommand dbCommand = db.GetStoredProcCommand("aspnet_SiteMap_AddPage");
            db.AddInParameter(dbCommand, "ApplicationId", DbType.Guid, Site.GetSite().ApplicationId);
            db.AddInParameter(dbCommand, "Title", DbType.String, _title);
            db.AddInParameter(dbCommand, "FileName", DbType.String, _fileName);
            db.AddInParameter(dbCommand, "Url", DbType.String, "~/" + _fileName);
            db.AddInParameter(dbCommand, "MetaKeywords", DbType.String, _metaKeywords);
            db.AddInParameter(dbCommand, "MetaDescription", DbType.String, _metaDescription);
            db.AddInParameter(dbCommand, "MenuText", DbType.String, _menuText);
            db.AddInParameter(dbCommand, "MenuHoverText", DbType.String, _menuHoverText);
            db.AddInParameter(dbCommand, "DisplayInMenu", DbType.Boolean, _displayInMenu);
            db.AddInParameter(dbCommand, "MasterPage", DbType.String, _masterPage);
            db.AddInParameter(dbCommand, "Visibility", DbType.Int32, _visibility);
            db.AddInParameter(dbCommand, "CreatedBy", DbType.Guid, Membership.User.GetUser(Thread.CurrentPrincipal.Identity.Name).ID);
            db.AddInParameter(dbCommand, "CreatedDate", DbType.DateTime, DateTime.Now);

            try
            {
                _id = int.Parse(db.ExecuteScalar(dbCommand).ToString());
                dbCommand.Dispose();
            }
            catch (Exception ex)
            {
                bool rethrow = ExceptionPolicy.HandleException(ex, "Data Access Layer Policy");
                if (rethrow)
                {
                    throw;
                }
            }

        }

        protected override void DataPortal_Update()
        {
            if (base.IsDirty)
            {
                if (_displayInMenuChanged)
                {
                    if (!_displayInMenu)
                    {
                        // remove the page and child pages from the menu
                        NavigationMenu.RemovePageFromMenu(_id);
                    }
                }

                Database db = DatabaseFactory.CreateDatabase("SiteWorx");
                DbCommand dbCommand = db.GetStoredProcCommand("aspnet_SiteMap_UpdatePage");
                db.AddInParameter(dbCommand, "ApplicationId", DbType.Guid, Site.GetSite().ApplicationId);
                db.AddInParameter(dbCommand, "PageId", DbType.Int32, _id);
                db.AddInParameter(dbCommand, "Title", DbType.String, _title);
                db.AddInParameter(dbCommand, "Url", DbType.String, _url);
                db.AddInParameter(dbCommand, "MetaKeywords", DbType.String, _metaKeywords);
                db.AddInParameter(dbCommand, "MetaDescription", DbType.String, _metaDescription);
                db.AddInParameter(dbCommand, "MenuText", DbType.String, _menuText);
                db.AddInParameter(dbCommand, "MenuHoverText", DbType.String, _menuHoverText);
                db.AddInParameter(dbCommand, "DisplayInMenu", DbType.Boolean, _displayInMenu);
                db.AddInParameter(dbCommand, "DisplayInMenuChanged", DbType.Boolean, _displayInMenuChanged);
                db.AddInParameter(dbCommand, "MasterPage", DbType.String, _masterPage);
                db.AddInParameter(dbCommand, "Visibility", DbType.Int32, _visibility);
                db.AddInParameter(dbCommand, "LastUpdatedBy", DbType.Guid, Membership.User.GetUser(Thread.CurrentPrincipal.Identity.Name).ID);
                db.AddInParameter(dbCommand, "LastUpdatedDate", DbType.DateTime, DateTime.Now);

                try
                {
                    db.ExecuteNonQuery(dbCommand);
                    dbCommand.Dispose();
                }
                catch (Exception ex)
                {
                    bool rethrow = ExceptionPolicy.HandleException(ex, "Data Access Layer Policy");
                    if (rethrow)
                    {
                        throw;
                    }
                }

            }
        }

        protected override void DataPortal_DeleteSelf()
        {
            DataPortal_Delete(new PageCriteria(ID, Url));
        }

        private void DataPortal_Delete(PageCriteria criteria)
        {
            // first need to check if you are displayed in the menu.
            PageInfo pageInfo = PageList.GetPageInfo(criteria.Path);
            if (pageInfo.DisplayInMenu)
            {
                // remove the page and child pages from the menu
                NavigationMenu.RemovePageFromMenu(criteria.ID);
            }
            
            Database db = DatabaseFactory.CreateDatabase("SiteWorx");
            DbCommand dbCommand = db.GetStoredProcCommand("aspnet_SiteMap_RemovePage");
            db.AddInParameter(dbCommand, "ApplicationId", DbType.Guid, Site.GetSite().ApplicationId);
            db.AddInParameter(dbCommand, "PageId", DbType.Int32, criteria.ID);
            db.AddInParameter(dbCommand, "Path", DbType.String, criteria.Path);

            try
            {
                db.ExecuteNonQuery(dbCommand);
                dbCommand.Dispose();
            }
            catch (Exception ex)
            {
                bool rethrow = ExceptionPolicy.HandleException(ex, "Data Access Layer Policy");
                if (rethrow)
                {
                    throw;
                }
            }

            // finally we need to delete the physical page file.
            File.Delete(HttpContext.Current.Server.MapPath(criteria.Path));

        }
      
        #endregion

    }
}
